#pragma once
#include "InetAddr.hpp"
#include "log.hpp"
#include <iostream>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include "Nocopy.hpp"
#include <cerrno>
#include "Comm.hpp"
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <functional>
#include "ThreadPool.hpp"
#include <pthread.h>
#include <unordered_set>

static const std::string defaultip = "0.0.0.0";
static const uint16_t defaultport = 8888;
static const int defaultfd = -1;
static const int defaultSize = 1024;

// using function_t = std::function<std::string(std::string)>;
using function_t = std::function<void()>;  // 绑定后无需参数

class UdpServer : public nocopy
{
private:
    // std::string _ip;
    uint16_t _port;
    int _sockfd; // 套接字文件描述符

    // function_t _OnMessage; // 回调
    pthread_mutex_t _server_mtx;
    std::vector<InetAddr> _online_user; // 多个用户同时访问
    std::unordered_set<std::string> _Hash_online_user_ip;
    std::unordered_set<uint16_t> _Hash_online_user_port;

public:
    UdpServer(const std::string &ip = defaultip, uint16_t port = defaultport)
        : /*  _ip(ip), */ _port(port)
    {
        pthread_mutex_init(&_server_mtx, nullptr);
    }

    ~UdpServer()
    {
        pthread_mutex_destroy(&_server_mtx);
    }

    void Init()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个网络通信的文件

        if (_sockfd < 0)
        {
            lg.LogMessage(Fatal, "socket error, %d : %s \n", errno, strerror(errno));
            exit(Socket_Err);
        }

        lg.LogMessage(Info, "socket success, socket: %d \n", _sockfd);

        // 绑定 指定网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // 将指定的内存清零
        local.sin_family = AF_INET;   // 告诉系统绑定的是网路通信的信息

        local.sin_port = htons(_port); // _port 是主机序列,需要主机转网络序列

        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // sin.adder 是一个结构体，这个结构体里面只有一个成员
        //   1. 将 _ip变为四字节ip  2. 变成网络序列

        // 实现IP地址的动态绑定   服务器不需要绑定ip地址
        local.sin_addr.s_addr = INADDR_ANY;
        //  宏值为0,这样只要客户端发过来的的套接字，端口号为 _port,无论ip地址，都能被进程收到

        // 结构体填完，设置到内核中

        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));

        if (n != 0)
        {
            // 失败
            lg.LogMessage(Fatal, "bind error, %d : %s \n", errno, strerror(errno));
            exit(Bind_Err);
        }

        // 添加线程池

        ThreadPool<function_t>::GetInstance()->Start();
    }

    void AddOnlineUser(InetAddr addr)
    {
        LockGuard lock(&_server_mtx);
        std::string addr_ip = addr.getip();
        uint16_t addr_port = addr.getport();
        if (_Hash_online_user_ip.count(addr_ip)&&_Hash_online_user_port.count(addr_port))
            return;

        _Hash_online_user_ip.insert(addr_ip);
        _Hash_online_user_port.insert(addr_port);
        //_online_user.emplace_back(addr);          ??
        _online_user.push_back(addr);
        lg.LogMessage(Debug, "%s:%d add to OnlineUser list", addr.getip(), addr.getport());
    }

    void Route(int sock, const std::string &message)
    {
        LockGuard lock(&_server_mtx);
        for (auto &user : _online_user)
        {
            // 实现消息的群发

            struct sockaddr_in local = user.getaddr();
            // 这里 getaddr() 返回的是 struct sockaddr_in 的副本，是一个临时对象
            // 而后面的操作中要对这个临时对象取地址，这式不可取的，所以在这里先拷贝一份，或者在 getaddr 的函数中返回引用

            sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&local, (socklen_t)sizeof user.getaddr());

            lg.LogMessage(Debug, "server send a message to %s:%d ,message:%s", user.getip(), user.getport(), message.c_str());
        }
    }

    void Start()
    {
        // 服务器不会退出,是一个死循环
        char buffer[defaultSize];
        for (;;)
        {
            struct sockaddr_in peer;      // 远端
            socklen_t len = sizeof(peer); // 不能乱写

            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            // 字符串方式
            if (n > 0)
            {
                InetAddr Addr(peer);
                AddOnlineUser(Addr); // 添加在线用户
                buffer[n] = 0;
                std::string message = "[";
                message += Addr.getip();
                message += ":";
                message += std::to_string(Addr.getport());
                message += "]# ";
                message += buffer;
                auto task = std::bind(&UdpServer::Route, this, _sockfd, message);  // 绑定函数的参数

                ThreadPool<function_t>::GetInstance()->Push(task);
                // 处理消息
                // Onmessage 处理消息，并将处理后的消息返回给 responce
                // std::string responce = _OnMessage(buffer);

                // std::cout << "client say# " << buffer << " " << std::endl;

                // 将处理完成的结果返回给客户端
                // sendto(_sockfd, responce.c_str(), responce.size(), 0, (struct sockaddr *)&peer, len);
            }
        }
    }
};
